package cn.duyo.spring.tx.transaction.support;

import cn.duyo.spring.tx.transaction.PlatformTransactionManager;
import cn.duyo.spring.tx.transaction.TransactionDefinition;
import cn.duyo.spring.tx.transaction.TransactionException;
import cn.duyo.spring.tx.transaction.TransactionStatus;

import java.io.Serializable;

/**
 * 事务管理的抽象实现类。采用同样的套路定义了事务的操作流程，分别是获取事务，事务提交，事务回滚。这三个步骤在不同的数据源上操作又有区别，所以该抽象类同时定义了需要子类去实际执行的抽象方法
 * @author du
 */
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {


    private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT;


    /**
     * 获取事务的方法：
     * - 根据当前是否已经有事务，如果有，根据定义的事务传播行为返回一个事务
     * - 如果没有，根据事务的定义返回一个事务
     * @param definition
     * @return
     * @throws TransactionException
     */
    @Override
    public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
        Object transaction = doGetTransaction();

        if (null == definition) {
            definition = new DefaultTransactionDefinition();
        }

        if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
            throw new TransactionException("Invalid transaction timeout " + definition.getTimeout());
        }

        try {
            //对于事务的传播行为暂时认为是默认的行为
            //创建事务
            DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true);

            doBegin(transaction, definition);

            return status;
        } catch (RuntimeException | Error ex) {

            throw ex;
        }

    }

    protected DefaultTransactionStatus newTransactionStatus(TransactionDefinition definition,
                                                            Object transaction, boolean newTransaction) {

        return new DefaultTransactionStatus(transaction, newTransaction);
    }

    protected int determineTimeout(TransactionDefinition definition) {
        if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
            return definition.getTimeout();
        }
        return this.defaultTimeout;
    }

    /**
     * 根据事务的状态，准备进行事务的提交操作
     * @param status
     * @throws TransactionException
     */
    @Override
    public void commit(TransactionStatus status) throws TransactionException {
        if (status.isCompleted()) {
            throw new IllegalArgumentException(
                    "Transaction is already completed - do not call or rollback more than once per transaction");
        }
        DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;


        processCommit(defStatus);

    }

    private void processCommit(DefaultTransactionStatus status) throws TransactionException {
        doCommit(status);
    }

    /**
     * 开始准备进行事务回滚
     * @param status
     * @throws TransactionException
     */
    @Override
    public void rollback(TransactionStatus status) throws TransactionException {
        if (status.isCompleted()) {
            throw new IllegalArgumentException(
                    "Transaction is already completed - do not call commit or rollback more than once per transaction");
        }
        DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
        processRollback(defStatus, false);
    }

    private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
        doRollback(status);
    }

    //---------------------------------------------------------------------
    // Abstract methods to be implemented by subclasses start
    //---------------------------------------------------------------------

    /**
     * 获取事务
     * 为当前的事务状态返回一个事务对象。得到该对象后在交给其他模版方法去处理
     */
    protected abstract Object doGetTransaction() throws TransactionException;


    /**
     * 挂起指定的事务
     * @param transaction
     * @return
     */
    protected abstract Object doSuspend(Object transaction);

    /**
     * 提交事务
     * 对于给定的事务进行提交操作
     */
    protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException;

    /**
     * 事务回滚
     * 对于指定的事务执行回滚操作
     */
    protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException;

    /**
     * 开始事务
     * 根据给定的事务定义开始一个新事务，在此之前要么没有事务，要么存在的事务已被挂起。所以可以放心大胆的开始一个新事务。
     */
    protected abstract void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException;

    //---------------------------------------------------------------------
    // Abstract methods to be implemented by subclasses end
    //---------------------------------------------------------------------


}